入社2週間でAWS SAMと格闘してハマったところ
サーバーレス開発部@大阪の岩田です。早いものでクラスメソッドに入社して2週間が経過しました。
先週から実際にプロジェクトに入って、SAMテンプレートをガリガリ書いているのですが、何かとハマることが多く、トライ&エラーを繰り返していました。
概ねテンプレートが作成できたので、備忘を兼ねてハマった箇所をまとめてみます。
概要
API GatewayからLambdaを起動し、DynamoDBにアクセスするという、至ってシンプルなREST APIを作成する案件です。
APIの仕様書をSwaggerで管理したいという要件があったため、APIの詳細についてはSAMテンプレートからSwaggerの定義ファイルを読み込む様な作りで作成しています。
SAMテンプレートでSwaggerを利用すると、AWS::Serverless::FunctionのEventだけでは定義できない様な詳細まで定義できるのですが、その分記述が複雑になり色々とハマってしまいました。
Invalid permissions on Lambda function
デプロイ完了後に、マネジメントコンソールからテストを行ったところ、
Invalid permissions on Lambda function
というエラーが発生し、テストに失敗しました。
Tue May 15 09:03:02 UTC 2018 : Sending request to https://lambda.ap-northeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:xxxxxx:function:xxxxxx/invocations Tue May 15 09:03:02 UTC 2018 : Execution failed due to configuration error: Invalid permissions on Lambda function Tue May 15 09:03:02 UTC 2018 : Method completed with status: 500
こちらに記載されているように、API GatewayにLambda の InvokeFunctionのアクセス許可が必要になります。 SAMテンプレートの中で、
SomeFunction: Type: AWS::Serverless::Function Properties: #略 Events: Get: Type: Api Properties: RestApiId: !Ref SomeApi Path: /hogehoge Method: GET
の様にEventsの中にType: Apiを記述していると、SAMがよしなにやってくれるのですが、Eventsを定義する際にPathやMethodを記述するのはSwaggerの定義と重複して冗長だな〜と思っていたので、Eventsを定義していませんでした。 こちらの本で紹介されているのと同じ書き方になります。↓
GitHubに上がっているSAMのソースコードを確認したところ、下記の様になっていました。イベントソースにAPIが指定されている場合は勝手にパーミッション作成までやってくれる様です。
class Api(PushEventSource): """Api method event source for SAM Functions.""" resource_type = 'Api' principal = 'apigateway.amazonaws.com' #略... def to_cloudformation(self, **kwargs): """If the Api event source has a RestApi property, then simply return the Lambda Permission resource allowing API Gateway to call the function. If no RestApi is provided, then additionally inject the path, method, and the x-amazon-apigateway-integration into the Swagger body for a provided implicit API. :param dict kwargs: a dict containing the implicit RestApi to be modified, should no explicit RestApi \ be provided. :returns: a list of vanilla CloudFormation Resources, to which this Api event expands :rtype: list """ resources = [] #略...
SAMテンプレートに下記の様な記述を追加し、API GatewayにLambda の InvokeFunctionを許可することで解決しています。
LambdaPermissionxxxx: Type: "AWS::Lambda::Permission" Properties: Action: lambda:InvokeFunction FunctionName: !Ref SomeLambdaFunction Principal: apigateway.amazonaws.com
Execution failed due to configuration error: Malformed Lambda proxy response
上記の対応でPOSTのAPIについてはテストが成功する様になったのですが、今度はGETのAPIで
Execution failed due to configuration error: Malformed Lambda proxy respons
というエラーが発生し、テストに失敗します。
こちらのドキュメントに記載があるのですが、 Swagger のAPI Gateway 拡張を利用してAPI GatewayからLambdaの呼び出しを設定する場合、API Gateway integrationオブジェクトのhttpMethodにはPOSTを設定する必要があります。
SAMテンプレートを下記の様に修正
x-amazon-apigateway-integrationのhttpMethodをGETからPOSTに変更することでエラーが改善しました。
x-amazon-apigateway-integration: uri: Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SomeLambdaFunction.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: "when_no_match" #httpMethod: "GET" httpMethod: "POST" contentHandling: "CONVERT_TO_TEXT" type: "aws_proxy"
API Gatewayのオーソライザーが設定されない
Cognitoで認証済みのユーザーのみAPIを利用させたい箇所があり、API GatewayのオーソライザーにCognitoユーザープールを指定しようとしていました。 Swaggerの定義は下記の様な内容です。
/xxxx/{id}: get: summary: "hogehoge" description: "hogehoge" security: - Cognito_Authorizer: [] #略 securityDefinitions: Cognito_Authorizer: type: "apiKey" name: "Authorization" in: "header" x-amazon-apigateway-authtype: cognito_user_pools x-amazon-apigateway-authorizer: providerARNs: - "arn:aws:cognito-idp:ap-northeast-1:xxxxxxxxxxx:userpool/ap-northeast-1_xxxxxxxx" type: cognito_user_pools
が、デプロイしてみると一向にオーソライザーが反映されません。
オチとしては type: "apiKey"
と書くべきところを、 type: "apikey"
と"k"を小文字で書いていたことが原因でした。
ただ、SAMでデプロイした際に、特にエラー等出なかったので、中々気付くことができませんでした。
この事象ですが、マネジメントコンソールから「Swaggerからインポート」を試すことでの原因を切り分け分けることができました。 SAMのデプロイではエラーが出なかったのですが、マネジメントコンソールからだと、下記の様なエラーメッセージが表示されたため、Swaggerの定義を微調整しながら原因を切り分けることができました。
まとめ
いかがだったでしょうか。 経験不足もあって、かなり苦戦しました。 同じ様な境遇のSAM初心者の方の参考になれば幸いです。